﻿using System;
using System.IO;
using System.Net;
using System.Linq;
using System.Text;
using System.Activities;
using System.Activities.Expressions;
using System.Activities.Presentation.PropertyEditing;
using System.Activities.Statements;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Markup;
using System.Xml;
using Regx = System.Text.RegularExpressions;

namespace Vision.Core.ActivityComponents.IO.Parsing
{
    /// <summary>
    /// 
    /// </summary>    
    [ContentProperty("Body")]
    [System.ComponentModel.Designer(typeof(TextFileParserDesigner))]
    public class TextFileParser : NativeActivity
    {
        /// <summary>
        /// 
        /// </summary>
        [Browsable(false)]
        [DefaultValue(null)]
        public ActivityAction<byte[]> Body { get; set; }
        
        /// <summary>
        /// 
        /// </summary>
        [Category("Input")]
        [RequiredArgument]
        [DefaultValue(null)]
        public InArgument<byte[]> ConfigurationFile { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [Category("Input")]
        [RequiredArgument]
        [DefaultValue(null)]
        public InArgument<byte[]> InputFile { get; set; }

        /// <summary>
        /// 
        /// </summary>
        [Category("Output")]
        public OutArgument<byte[]> OutputFile { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public TextFileParser(): base()
        {
            this.Body = new ActivityAction<byte[]>(){
                Argument = new DelegateInArgument<byte[]>() { Name = "bytes" }
            };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="metadata"></param>
        protected override void CacheMetadata(NativeActivityMetadata metadata)
        {            
            metadata.AddArgument(new RuntimeArgument("ConfigurationFile", typeof(byte[]), ArgumentDirection.In, true));
            metadata.AddArgument(new RuntimeArgument("InputFile", typeof(byte[]), ArgumentDirection.In, true));
            metadata.AddArgument(new RuntimeArgument("OutputFile", typeof(byte[]), ArgumentDirection.Out, false));
            
            metadata.AddDelegate(this.Body);   
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        protected override void Execute(NativeActivityContext context)
        {
            byte[] outputFile = null;
            string inputDoc = string.Empty;
            XmlDocument configXml = new XmlDocument();
            XmlDocument outputXml = new XmlDocument();

            //Load input document
            using (MemoryStream mStream = new MemoryStream(context.GetValue(this.InputFile))){
                using (StreamReader reader = new StreamReader(mStream)){
                    inputDoc = reader.ReadToEnd();
                }
            }
            
            //Load config xml document
            using (MemoryStream mStream = new MemoryStream(context.GetValue(this.ConfigurationFile))){
                configXml.Load(mStream);
            }

            //Process textfile
            XmlNode rootNode = outputXml.CreateNode(XmlNodeType.Element, "parseoutput", "");
            this.ProcessNode(configXml.DocumentElement.FirstChild, rootNode, inputDoc, null);
            outputXml.AppendChild(rootNode);

            //Save output file            
            using (MemoryStream mStream = new MemoryStream()){
                outputXml.Save(mStream);
                outputFile = mStream.ToArray();                
            }

            if (this.Body != null && this.Body.Handler != null){
                context.ScheduleAction(this.Body, outputFile, this.OnBodyCompleted, this.OnBodyFaulted);    
            }

            if (this.OutputFile != null){
                context.SetValue(this.OutputFile, outputFile);
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="configNode"></param>
        /// <param name="outputNode"></param>
        /// <param name="inputText"></param>
        /// <param name="matchCollection"></param>
        private void ProcessNode(XmlNode configNode, XmlNode outputNode, string inputText, Regx.GroupCollection groups)
        {
			Regx.MatchCollection matches = null;
			XmlNode newNode = null;
			string[] splits = null;

            switch (configNode.Name.ToLower()) {
                case "parse":
                    switch (configNode.Attributes["command"].Value.ToLower()){
                        case "split":
                            splits = Regx.Regex.Split(inputText, configNode.Attributes["expression"].Value);
                            foreach (string s in splits){
                                foreach (XmlNode node in configNode.ChildNodes){
                                    this.ProcessNode(node, outputNode, s, null);
                                }
                            }
                            break;
						case "split-multipleline":
							splits = Regx.Regex.Split(inputText, configNode.Attributes["expression"].Value, Regx.RegexOptions.Multiline);
							foreach (string s in splits)
							{
								foreach (XmlNode node in configNode.ChildNodes)
								{
									this.ProcessNode(node, outputNode, s, null);
								}
							}
							break;
                        case "match":
                            matches = Regx.Regex.Matches(inputText, configNode.Attributes["expression"].Value);
                            foreach (Regx.Match match in matches){
                                foreach (XmlNode node in configNode.ChildNodes){
                                    this.ProcessNode(node, outputNode, match.Value, match.Groups);
                                }                                       
                            }
                            break;
                        case "replace":
                            inputText = Regx.Regex.Replace(inputText, configNode.Attributes["expression"].Value, configNode.Attributes["parameter"].Value);
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }                                   
                            break;
                        case "remove":                            
                            inputText = Regx.Regex.Replace(inputText, configNode.Attributes["expression"].Value, string.Empty);
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }       
                            break;
                        case "trim":
                            inputText = inputText.Trim();
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }       
                            break;
                        case "groupvalue":
                            inputText = groups[configNode.Attributes["expression"].Value].Value;
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }       
                            break;                        
                        case "htmlencode":
                            inputText = System.Web.HttpUtility.HtmlEncode(inputText);
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }       
                            break;
						case "substring":
							inputText = inputText.Substring(int.Parse(configNode.Attributes["startindex"].Value), int.Parse(configNode.Attributes["length"].Value));
							foreach (XmlNode node in configNode.ChildNodes) {
								this.ProcessNode(node, outputNode, inputText, null);
							}
							break;
						case "formatdate":
							inputText = DateTime.ParseExact(inputText, configNode.Attributes["originalformat"].Value, System.Globalization.CultureInfo.InvariantCulture).ToString(configNode.Attributes["outputformat"].Value);
							foreach (XmlNode node in configNode.ChildNodes) {
								this.ProcessNode(node, outputNode, inputText, null);
							}
							break;
						case "notempty":
							if (inputText != string.Empty)
							{
								foreach (XmlNode node in configNode.ChildNodes)
								{
									this.ProcessNode(node, outputNode, inputText, null);
								}
							}
							break;
                        default:
                            foreach (XmlNode node in configNode.ChildNodes){
                                this.ProcessNode(node, outputNode, inputText, null);
                            }       
                            break;
                    }                                        
                    break;
                case "element":
					newNode = outputNode.OwnerDocument.CreateNode(XmlNodeType.Element, configNode.Attributes["name"].Value, "");

                    if (configNode.Attributes["value"] != null){
                        newNode.InnerText = inputText;
                    }
                    
                    foreach (XmlNode node in configNode.ChildNodes){
                        this.ProcessNode(node, newNode, inputText, groups);
                    }
                    outputNode.AppendChild(newNode);
                    break;
                case "attribute":
                    XmlAttribute newAttribute = outputNode.OwnerDocument.CreateAttribute(configNode.Attributes["name"].Value);                                        
                    if (configNode.Attributes["value"] != null){
                        newAttribute.Value = inputText;                        
                    }                    
                    outputNode.Attributes.Append(newAttribute);                    
                    break;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="instance"></param>
        private void OnBodyCompleted(NativeActivityContext context, ActivityInstance instance)
        { 
        
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="faultContext"></param>
        /// <param name="propagatedException"></param>
        /// <param name="propagatedFrom"></param>
        private void OnBodyFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
        {
        
        }
    }
}
